//
// Copyright (c) 2006 by Grant Yoshida -- Modified for Bandleader's purposes.
//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Thanks to:     Erik Neuenschwander <erikn@leland.stanford.edu>
//                   for Windows 95 assembly code for Pentium clock cycles.
//                Ozgur Izmirli <ozgur@ccrma.stanford.edu> 
//                   for concept of periodic timer.
// Creation Date: Mon Oct 13 11:34:57 GMT-0800 1997
// Last Modified: Tue Feb 10 21:05:19 GMT-0800 1998
// Last Modified: Sat Sep 19 15:56:48 PDT 1998
// Last Modified: Mon Feb 22 04:44:25 PST 1999
// Last Modified: Sun Nov 28 12:39:39 PST 1999   (added adjustPeriod())
// Filename:      .../sig/code/control/SigTimer/SigTimer.cpp
// Web Address:   http://www-ccrma.stanford.edu/~craig/improv/src/SigTimer.cpp
// Syntax:        C++ 
//
// Description:   This class can only be used on Motorola Pentinum 75 Mhz
//                chips or better because the timing information is
//                extracted from the clock cycle count from a register
//                on the CPU itself.  This class will estimate the 
//                speed of the computer, but it would be better if there
//                was a way of finding out the speed from some function.
//                This class is used primarily for timing of MIDI input 
//                and output at a millisecond resolution.
//

#include "SigTimer.h"

#include <stdlib.h>
#include <sstream>
#include <stdexcept>

// get Sleep or usleep function definition for millisleep function:
#ifdef VISUAL
   #define WIN32_LEAN_AND_MEAN
   #include <windows.h>             
   #undef WIN32_LEAN_AND_MEAN
#else
   #include <unistd.h>
#endif

#include <iostream>
using namespace std;

//////////////////////////////
//
// SigTimer::SigTimer
//

SigTimer::SigTimer(void) :
    m_time(),
    offset(0),              // initialize the start time of timer
    ticksPerSecond(1000),   // default of 1000 ticks per second
    period(1000.0)          // default period of once per second
{
    m_time.start();
}


SigTimer::SigTimer(int aSpeed) :
    m_time(),
    offset(0),              // initialize the start time of timer
    ticksPerSecond(1000),   // default of 1000 ticks per second
    period(1000.0)          // default period of once per second
{
    m_time.start();
}


SigTimer::SigTimer(SigTimer& aTimer) :
    m_time(aTimer.m_time),
    offset(aTimer.offset),
    ticksPerSecond(aTimer.ticksPerSecond),
    period(aTimer.period)
{
}



//////////////////////////////
//
// SigTimer::~SigTimer
//

SigTimer::~SigTimer() {
   // do nothing
}



//////////////////////////////
//
// SigTimer::adjustPeriod -- adjust the period of the timer.
//

void SigTimer::adjustPeriod(double periodDelta)
{
   offset -= (int)(getPeriod() * periodDelta / getTicksPerSecond());
}



//////////////////////////////
//
// SigTimer::timeElasped -- returns the number of seconds elapsed since the class was constructed
//

int SigTimer::timeElasped() const {
    return m_time.elapsed();
}



//////////////////////////////
//
// SigTimer::expired -- returns the integral number of periods
//    That have passed since the last update or reset.
//    See getPeriodCount which returns a floating point
//    count of the period position.
//


int SigTimer::expired(void) const {
   return (int)(getTime()/period);
}



//////////////////////////////
//
// SigTimer::getPeriod -- returns the timing period of the timer,
//    if the timer is being used as a periodic timer.
//

double SigTimer::getPeriod(void) const {
   return period;
}



//////////////////////////////
//
// SigTimer::getPeriodCount -- returns the current period count
//    of the timer as a double.  Similar in behavior to expired
//    function but lists the current fraction of a period.
//

double SigTimer::getPeriodCount(void) const {
   return (double)getTime()/period;
}



//////////////////////////////
//
// SigTimer::getTempo -- returns the current tempo in terms
//     of beats (ticks) per minute.
//

double SigTimer::getTempo(void) const {
   return getTicksPerSecond() * 60.0 / getPeriod();
}



//////////////////////////////
//
// SigTimer::getTicksPerSecond -- return the number of ticks per
//     second.
//

int SigTimer::getTicksPerSecond(void) const {
   return ticksPerSecond;
}



//////////////////////////////
//
// SigTimer::getTime -- returns time in milliseconds by default.  
// 	time can be altered to be in some other unit of time
//	by using the setTicksPerSecond function.
//	(Default is 1000 ticks per second.)
//

int SigTimer::getTime(void) const {
   return timeElasped()-offset;
}
   


//////////////////////////////
//
// SigTimer::getTimeInSeconds 
//

double SigTimer::getTimeInSeconds(void) const {
   return (timeElasped()-offset) / 1000.0;
}
   


//////////////////////////////
//
// SigTimer::reset -- set the timer to 0.
//

void SigTimer::reset(void) {
   offset = timeElasped();
}


//////////////////////////////
//
// SigTimer::setPeriod -- sets the period length of the timer.
//    input value cannot be less than 1.0.
//

void SigTimer::setPeriod(double aPeriod) {
   if (aPeriod < 1.0) {
      std::ostringstream stream;
      stream << "Error: period too small: " << aPeriod;
      throw std::logic_error(stream.str());
   }
   period = aPeriod;
}



//////////////////////////////
//
// SigTimer::setPeriodCount -- adjusts the offset time according
//     to the current period to match the specified period count.
//

void SigTimer::setPeriodCount(double aCount) {
   offset = (int)(timeElasped() -
                    aCount * getPeriod() * 1.0 / getTicksPerSecond());
}



//////////////////////////////
//
// SigTimer::setTempo -- sets the period length in terms of 
//	beats per minute.
//

void SigTimer::setTempo(double beatsPerMinute) {
   if (beatsPerMinute < 1.0) {
      std::ostringstream stream;
      stream << "Error: tempo is too slow: " << beatsPerMinute;
      throw std::logic_error(stream.str());
   }
   double count = getPeriodCount();
   period = getTicksPerSecond() * 60.0 / beatsPerMinute;
   setPeriodCount(count);
}



//////////////////////////////
//
// SigTimer::setTicksPerSecond 
//

void SigTimer::setTicksPerSecond(int aTickRate) {
   if (aTickRate <= 0) {
      std::ostringstream stream;
      stream << "Error: Cannot set the tick rate to be negative: " << aTickRate;
      throw std::logic_error(stream.str());
   }
   ticksPerSecond = aTickRate;
}



//////////////////////////////
//
// SigTimer::start 
//

void SigTimer::start(void) {
   reset();
}



//////////////////////////////
//
// SigTimer::sync 
//

void SigTimer::sync(SigTimer& aTimer) {
   offset = aTimer.offset;
}



//////////////////////////////
//
// SigTimer::update -- set the timer start to the next period.
//

void SigTimer::update(void) {
   if (getTime() >= getPeriod()) {
      offset += (int)(getPeriod() * getFactor());
   }
}


// update(int) will do nothing if the periodCount is greater than
// than the expired period count

void SigTimer::update(int periodCount) {
   if (periodCount > expired()) {
      return;
   } else {
      offset += (int)(getPeriod() * getFactor() * periodCount);
   }
}



///////////////////////////////////////////////////////////////////////////
//
// protected functions:
//


//////////////////////////////
//
// SigTimer::getFactor -- 
//

double SigTimer::getFactor(void) const {
   return (double)(1.0/(double)getTicksPerSecond());
}



///////////////////////////////////////////////////////////////////////////
//
// Miscellaneous timing functions are located here untill a better
// place can be found:
//

void millisleep(int milliseconds) {
   #ifdef VISUAL
      Sleep(milliseconds);
   #else
      usleep( milliseconds * 1000);
   #endif
}
      

void millisleep(float milliseconds) {
   #ifdef VISUAL
      // cannot convert to microseconds in Visual C++ yet.
      // Tell me how and I'll fix the following line
      Sleep((unsigned long)milliseconds);
   #else
      usleep((int)(milliseconds * 1000.0));
   #endif
}
      
  
  

// md5sum:	b35e9e6a8d6fd16636d7fca5d565f284  - SigTimer.cpp =css= 20030102
